Aller au contenu principal

Création d'un composant

Créer un composant "Symfony UX" pour Quiz interactif

Schéma

Notions théoriques

Qu'est-ce qu'un composant Twig dans Symfony UX ?

Symfony UX propose 2 types de composants réutilisables côté template : les Twig Components et les Live Components.

Dans ce cours, on travaille avec les Twig Components, qui constituent la base de tout composant Symfony UX personnalisé.

Un Twig Component est une classe PHP associée à un template Twig. Ensemble, ils forment une unité autonome et réutilisable que l'on peut appeler depuis n'importe quel template de l'application, comme une balise HTML personnalisée.

L'idée est simple : plutôt que de copier-coller du HTML et de la logique PHP dans plusieurs templates, on encapsule tout dans un composant.

On l'appelle ensuite avec une syntaxe concise :

<twig:Quiz :questions="questions" />

Cette ligne suffit à afficher un quiz complet, avec toute sa logique et son rendu visuel.

Les 2 packages nécessaires

Pour créer des composants Twig dans Symfony UX, il faut installer deux packages :

  • symfony/ux-twig-component : fournit la classe de base AbstractComponent et le système de rendu des composants Twig
  • symfony/ux-stimulus-bundle : fournit Stimulus pour gérer les interactions JavaScript (déjà installé avec --webapp)
composer require symfony/ux-twig-component
info

Si le projet a été créé avec symfony new mon-projet --webapp, le bundle Stimulus est déjà installé. Seul ux-twig-component peut manquer.

La structure d'un Twig Component

Un composant Twig se compose de deux fichiers :

1. La classe PHP — placée dans src/Twig/Components/

// src/Twig/Components/Alert.php
namespace App\Twig\Components;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

#[AsTwigComponent]
class Alert
{
public string $type = 'info';
public string $message = '';
}

2. Le template Twig — placé dans templates/components/

{# templates/components/Alert.html.twig #}
<div class="alert alert-{{ type }}">
{{ message }}
</div>
astuce

La convention de nommage est stricte : si la classe s'appelle Alert, le template doit s'appeler Alert.html.twig dans templates/components/. Symfony UX fait le lien automatiquement grâce à l'attribut #[AsTwigComponent].

L'attribut PHP #[AsTwigComponent]

L'attribut #[AsTwigComponent] (introduit en PHP 8.0) est ce qui transforme une classe PHP ordinaire en composant Twig. Il indique à Symfony UX que cette classe est un composant et lui permet de :

  • Détecter automatiquement le template associé
  • Exposer les propriétés publiques de la classe comme variables Twig
  • Permettre l'appel du composant depuis un template avec <twig:NomDuComposant />

On peut aussi personnaliser le nom du composant :

#[AsTwigComponent('mon-quiz')]
class Quiz { ... }

Ce qui permettrait de l'appeler avec <twig:mon-quiz />.

Les propriétés publiques : le pont PHP/Twig

Les propriétés publiques de la classe PHP sont automatiquement accessibles dans le template Twig du composant. C'est le mécanisme central de la communication entre la logique PHP et le rendu HTML.

#[AsTwigComponent]
class Quiz
{
public string $titre = 'Quiz sans titre';
public array $questions = [];
public bool $afficherScore = true;
}

Dans le template, on accède directement à titre, questions et afficherScore sans déclaration supplémentaire.

remarque

Seules les propriétés publiques sont exposées au template. Les propriétés protected et private restent inaccessibles depuis Twig — elles peuvent servir pour la logique interne de la classe.

Passer des données au composant

Lorsqu'on appelle un composant depuis un template parent, on lui passe des données via des attributs HTML-like :

{# Appel avec des valeurs statiques #}
<twig:Alert type="danger" message="Une erreur est survenue" />

{# Appel avec des variables Twig (préfixe : pour évaluer l'expression) #}
<twig:Alert :type="alertType" :message="alertMessage" />

Le préfixe : avant un attribut indique à Symfony UX d'évaluer l'expression Twig passée en valeur, plutôt que de la traiter comme une chaîne de caractères.

attention

Sans le préfixe :, la valeur est toujours interprétée comme une chaîne de caractères. type="danger" passe la chaîne "danger", tandis que :type="monType" passe la valeur de la variable Twig monType.

Les méthodes dans un composant

Une classe de composant peut contenir des méthodes publiques, accessibles depuis le template :

#[AsTwigComponent]
class Quiz
{
public array $questions = [];

public function getNombreQuestions(): int
{
return count($this->questions);
}
}

Dans le template :

<p>Ce quiz contient {{ this.getNombreQuestions() }} question(s).</p>
info

Dans le template d'un composant, this désigne l'instance de la classe PHP du composant. On accède aux méthodes avec this.nomDeLaMethode() et aux propriétés avec nomDeLaPropriete.

Ajouter de l'interactivité avec Stimulus

Un Twig Component est statique : il génère du HTML côté serveur, sans interaction côté client. Pour ajouter de l'interactivité (comme révéler la bonne réponse au clic), on utilise Stimulus en ajoutant des attributs data-controller et data-action dans le template du composant.

<div data-controller="quiz">
<button data-action="click->quiz#verifier">Vérifier ma réponse</button>
<p data-quiz-target="resultat"></p>
</div>

Et dans un contrôleur Stimulus créé dans assets/controllers/quiz_controller.js :

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
static targets = ['resultat'];

verifier() {
this.resultatTarget.textContent = 'Bonne réponse !';
}
}
astuce

Cette combinaison Twig Component (logique PHP + rendu HTML) + Stimulus (interactivité JS) est le cœur de Symfony UX. Le PHP gère les données et la structure, JavaScript gère uniquement ce qui ne peut pas se faire côté serveur.


Exemple pratique

Création d'un composant Quiz complet

Cet exemple crée un composant Quiz réutilisable qui affiche une série de questions à choix multiple et révèle la bonne réponse au clic, grâce à Stimulus.

Étape 1 — Installer ux-twig-component

composer require symfony/ux-twig-component

Étape 2 — Créer la classe PHP du composant

Créer le fichier src/Twig/Components/Quiz.php :

<?php

namespace App\Twig\Components;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

#[AsTwigComponent]
class Quiz
{
public string $titre = 'Quiz';

/** @var array<array{question: string, options: string[], answer: string}> */
public array $questions = [];

public function getNombreQuestions(): int
{
return count($this->questions);
}
}

Étape 3 — Créer le template Twig du composant

Créer le fichier templates/components/Quiz.html.twig :

<div class="quiz-wrapper" data-controller="quiz">
<h2>{{ titre }}</h2>
<p><em>{{ this.getNombreQuestions() }} question(s)</em></p>

{% for index, q in questions %}
<div class="quiz-question" data-quiz-target="question" style="margin-bottom: 1.5rem; padding: 1rem; border: 1px solid #ddd; border-radius: 6px;">
<p><strong>{{ index + 1 }}. {{ q.question }}</strong></p>

{% for option in q.options %}
<label style="display: block; margin: 0.3rem 0; cursor: pointer;">
<input
type="radio"
name="question_{{ index }}"
value="{{ option }}"
data-answer="{{ q.answer }}"
data-action="change->quiz#selectionner"
data-quiz-target="option"
>
{{ option }}
</label>
{% endfor %}

<p data-quiz-target="feedback{{ index }}" style="margin-top: 0.5rem; font-weight: bold;"></p>
</div>
{% endfor %}
</div>

Étape 4 — Créer le contrôleur Stimulus

Créer le fichier assets/controllers/quiz_controller.js :

import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
selectionner(event) {
const input = event.target;
const reponseChoisie = input.value;
const bonnereponse = input.dataset.answer;

// Trouver l'index de la question via le nom du champ radio
const nom = input.name; // ex: "question_0"
const index = nom.split('_')[1];

// Trouver le paragraphe feedback correspondant
const feedbackTarget = this.element.querySelector(
`[data-quiz-target="feedback${index}"]`
);

if (!feedbackTarget) return;

if (reponseChoisie === bonnereponse) {
feedbackTarget.textContent = '✓ Bonne réponse !';
feedbackTarget.style.color = 'green';
} else {
feedbackTarget.textContent = `✗ Mauvaise réponse. La bonne réponse était : ${bonnereponse}`;
feedbackTarget.style.color = 'red';
}

// Désactiver tous les boutons radio de cette question
const radios = this.element.querySelectorAll(`input[name="${nom}"]`);
radios.forEach(r => r.disabled = true);
}
}

Étape 5 — Utiliser le composant dans un contrôleur Symfony

Créer un contrôleur de test :

php bin/console make:controller QuizDemoController

Modifier src/Controller/QuizDemoController.php :

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class QuizDemoController extends AbstractController
{
#[Route('/quiz-demo', name: 'app_quiz_demo')]
public function index(): Response
{
$questions = [
[
'question' => 'Quel attribut PHP active un Twig Component ?',
'options' => ['#[Component]', '#[AsTwigComponent]', '#[TwigComponent]', '#[UxComponent]'],
'answer' => '#[AsTwigComponent]',
],
[
'question' => 'Où place-t-on les classes PHP des Twig Components ?',
'options' => ['src/Components/', 'src/Twig/Components/', 'src/UX/', 'templates/components/'],
'answer' => 'src/Twig/Components/',
],
[
'question' => 'Quel préfixe évalue une expression Twig dans un attribut de composant ?',
'options' => ['@', '$', '!', ':'],
'answer' => ':',
],
];

return $this->render('quiz_demo/index.html.twig', [
'questions' => $questions,
]);
}
}

Étape 6 — Appeler le composant dans le template

Modifier templates/quiz_demo/index.html.twig :

{% extends 'base.html.twig' %}

{% block title %}Démo Quiz{% endblock %}

{% block body %}
<div style="max-width: 700px; margin: 2rem auto; padding: 0 1rem;">
<h1>Démonstration du composant Quiz</h1>

<twig:Quiz
titre="Quiz Symfony UX"
:questions="questions"
/>
</div>
{% endblock %}

Étape 7 — Tester

symfony server:start

Ouvrir https://127.0.0.1:8000/quiz-demo. Chaque question affiche ses options sous forme de boutons radio. Au clic sur une option, le feedback s'affiche immédiatement en vert ou en rouge, et les options sont désactivées pour empêcher un second choix.

astuce

Le composant est entièrement réutilisable. Pour afficher un second quiz sur la même page, il suffit d'appeler <twig:Quiz titre="Quiz 2" :questions="autresQuestions" /> avec un autre tableau de questions.


Test de mémorisation/compréhension


Quel attribut PHP est indispensable pour déclarer une classe comme Twig Component ?


Dans quel dossier doit se trouver le template d'un Twig Component nommé `Quiz` ?


Comment accède-t-on aux méthodes de la classe PHP depuis le template d'un composant ?


Que signifie le préfixe `:` dans `<twig:Quiz :questions="mesQuestions" />` ?


Quelles propriétés d'une classe de composant sont automatiquement accessibles dans son template Twig ?


Dans quel dossier place-t-on les classes PHP des Twig Components par convention ?


Quel package Composer fournit le système de Twig Components dans Symfony UX ?


Quelle syntaxe Twig permet d'appeler un composant nommé `Quiz` depuis un template parent ?


Dans le template d'un composant, quelle directive Stimulus déclenche une méthode au clic ?


Un Twig Component statique génère son HTML à quel moment ?



TP "Alerte de notification Symfony UX"

Dans ce TP, vous allez créer un composant Twig réutilisable nommé Alerte, capable d'afficher des messages de notification colorés (succès, erreur, avertissement, info). Un bouton de fermeture animé, géré par un contrôleur Stimulus, permettra de masquer l'alerte avec une transition d'opacité. Le composant sera intégré dans une page de démonstration.


Étape 1 — Créer le projet et installer les dépendances

Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :

cd %USERPROFILE%\Documents
symfony new tp-alerte-notification --webapp
cd tp-alerte-notification
code .

Une fois VS Code ouvert sur le projet, installez le package ux-twig-component :

composer require symfony/ux-twig-component

Vérifiez que l'installation s'est bien déroulée en cherchant symfony/ux-twig-component dans composer.json.


Bonne pratique - Inspecter les fichiers modifiés par Flex

Après chaque composer require, tapez git diff dans le terminal pour voir exactement quels fichiers ont été modifiés par la recette Flex. C'est le moyen le plus fiable de comprendre ce que Flex a configuré automatiquement (mise à jour de controllers.json, de importmap.php, création de fichiers de configuration, etc.).


Étape 2 — Créer la classe PHP du composant Alerte

Créez le fichier src/Twig/Components/Alerte.php (créez le dossier Twig/Components si nécessaire).

Ce composant doit exposer :

  • Une propriété type (string, 'info' par défaut) : détermine la couleur de l'alerte
  • Une propriété message (string, '' par défaut) : le texte à afficher
  • Une propriété dismissible (bool, true par défaut) : affiche ou non le bouton de fermeture
  • Une méthode getCssClass() : retourne la classe CSS Bootstrap correspondant au type

Bonne pratique - Valeurs par défaut dans un composant

Définissez toujours des valeurs par défaut cohérentes pour les propriétés publiques d'un composant. Ici, type = 'info' et dismissible = true garantissent qu'un <twig:Alerte /> appelé sans aucun attribut reste visuellement correct. Un composant sans valeurs par défaut oblige l'appelant à fournir tous les attributs — ce qui le rend fragile.


Étape 3 — Créer le template Twig du composant

Créez le fichier templates/components/Alerte.html.twig.

Le template doit :

  • Positionner data-controller="alerte" directement sur la div de l'alerte (permet à Stimulus d'agir sur l'alerte entière via this.element)
  • Utiliser this.getCssClass() pour les classes CSS dynamiques
  • Afficher le message dans un <span>
  • Afficher conditionnellement un bouton de fermeture avec data-action="click->alerte#fermer"

Bonne pratique - data-controller sur l'élément racine vs cibles internes

Quand le contrôleur Stimulus doit agir sur l'élément entier (le masquer, le supprimer, l'animer), placez data-controller sur cet élément directement. this.element y référence automatiquement. Réservez static targets aux éléments internes à retrouver parmi plusieurs descendants (champ de texte, compteur, zone de feedback, etc.).


Étape 4 — Créer le contrôleur Stimulus avec animation

Créez le fichier assets/controllers/alerte_controller.js.

La méthode fermer() doit animer la disparition de l'alerte en trois temps :

  1. Activer une transition CSS opacity 0.3s sur this.element
  2. Mettre l'opacité à 0 (déclenche l'animation)
  3. Supprimer l'élément du DOM après 300 ms (durée de l'animation)

Bonne pratique - Synchroniser setTimeout et la transition CSS

La durée du setTimeout doit correspondre exactement à la durée de la transition CSS. Si la transition est 0.3s (300 ms), le setTimeout doit valoir 300. Extraire cette valeur dans une constante (const DUREE_MS = 300) évite les bugs de désynchronisation si on change la durée de l'animation.


Étape 5 — Créer le contrôleur Symfony et la page de démonstration

Générez un contrôleur de démonstration :

php bin/console make:controller AlerteController

Modifiez src/Controller/AlerteController.php pour préparer un tableau de quatre alertes de types différents et les transmettre au template :


Bonne pratique - Le contrôleur ne connaît pas le composant

Le contrôleur Symfony ne doit jamais référencer directement un composant Twig. Son rôle est de préparer des données structurées et de les transmettre au template. C'est le template qui décide du rendu, y compris en déléguant à des composants réutilisables. Cette séparation facilite les tests unitaires du contrôleur et le remplacement du composant sans toucher au contrôleur.


Étape 6 — Vérifier la configuration et tester

Ouvrez templates/base.html.twig et vérifiez la présence de {{ importmap('app') }} dans le <head>. Lancez ensuite le serveur :

symfony server:start

Accédez à https://127.0.0.1:8000/alerte et vérifiez :

  1. Les quatre alertes s'affichent avec des couleurs distinctes selon leur type
  2. Un clic sur × déclenche une animation d'opacité puis supprime l'alerte
  3. La cinquième alerte (non fermable) n'affiche pas de bouton ×
  4. La console navigateur (F12) ne contient aucune erreur JavaScript

Bonne pratique - Inspecter le HTML généré par un composant

Pour déboguer un composant Twig, l'outil le plus rapide est l'inspecteur du navigateur (F12 → Elements). Le HTML généré révèle exactement ce que Symfony UX a produit : les attributs data-*, les classes CSS, la structure. Si data-controller est absent, le problème est dans le template Twig. Si le contrôleur est présent mais ne réagit pas, le problème est côté JavaScript (nom de fichier, importmap).

Une solution complète du TP

Une solution

TP "AvisUtilisateur Symfony UX"

Dans ce TP, vous allez créer un composant Twig réutilisable nommé AvisUtilisateur, capable d'afficher une note sous forme d'étoiles colorées (jaunes pour les étoiles actives, grises pour les inactives) accompagnée d'un commentaire.

Le composant sera ensuite utilisé dans une page de démonstration affichant plusieurs avis sur un produit fictif.


Étape 1 — Créer le projet et installer les dépendances

Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :

cd %USERPROFILE%\Documents
symfony new tp-avis-utilisateur --webapp
cd tp-avis-utilisateur
code .

Une fois VS Code ouvert sur le projet, installez le package ux-twig-component :

composer require symfony/ux-twig-component

Vérifiez que l'installation s'est correctement déroulée en cherchant symfony/ux-twig-component dans le fichier composer.json.


Bonne pratique - Vérifier une installation Composer

Après chaque composer require, vérifiez toujours que le package apparaît dans composer.json ET que Symfony Flex a bien exécuté sa recette (message dans le terminal). La recette peut créer des fichiers de configuration automatiquement — vérifiez également config/bundles.php pour confirmer l'enregistrement du bundle.


Étape 2 — Créer la classe PHP du composant AvisUtilisateur

Créez le fichier src/Twig/Components/AvisUtilisateur.php.

Ce composant doit exposer les propriétés et méthodes suivantes :

  • Une propriété publique auteur de type string, valeur par défaut 'Anonyme'
  • Une propriété publique note de type int, valeur par défaut 5 (valeur entre 1 et 5)
  • Une propriété publique commentaire de type string, valeur par défaut ''
  • Une propriété publique date de type string, valeur par défaut ''
  • Une méthode publique getEtoiles() qui retourne un tableau de 5 éléments, chacun étant un tableau associatif avec une clé active (booléen) indiquant si l'étoile doit être jaune ou grise

Bonne pratique - Propriétés publiques comme interface du composant

Dans un Twig Component, les propriétés publiques constituent l'interface publique du composant — elles sont directement accessibles dans le template Twig sans configuration supplémentaire. Définissez toujours des valeurs par défaut sensées pour rendre le composant utilisable même sans paramètres obligatoires. Réservez les méthodes publiques aux calculs dérivés (comme getEtoiles()), appelables via this.methode() dans Twig.


Étape 3 — Créer le template Twig du composant

Créez le fichier templates/components/AvisUtilisateur.html.twig. Ce template doit afficher :

  • Le nom de l'auteur
  • Les 5 étoiles (étoile pleine en jaune si active, en gris clair si inactive)
  • La note sous forme textuelle, par exemple 3 / 5
  • Le commentaire de l'utilisateur
  • La date de l'avis (si elle est renseignée)

Le tout doit être contenu dans une <div> avec un style carte (bordure, arrondi, ombre légère).


Bonne pratique - Méthodes vs propriétés dans les templates Twig

Dans un Twig Component, les propriétés publiques s'accèdent directement ({{ auteur }}), tandis que les méthodes publiques nécessitent le préfixe this. ({{ this.getEtoiles() }}). Utilisez les méthodes pour les calculs dérivés ou les transformations de données — gardez les propriétés pour les données brutes transmises par l'appelant.


Étape 4 — Créer le contrôleur Symfony

Générez un contrôleur nommé AvisController :

php bin/console make:controller AvisController

Modifiez src/Controller/AvisController.php pour préparer un tableau de cinq avis fictifs sur un produit et le transmettre au template. Chaque avis doit contenir les clés auteur, note, commentaire et date.


Bonne pratique - Séparation contrôleur / composant

Le contrôleur Symfony ne doit pas connaître l'existence des composants Twig utilisés dans ses templates. Son rôle est de préparer et transmettre des données structurées. C'est le template qui décide comment afficher ces données, y compris en délégant à des composants. Cette séparation facilite la réutilisation et les tests unitaires du contrôleur.


Étape 5 — Créer le template de la page

Modifiez templates/avis/index.html.twig pour itérer sur le tableau avis et appeler le composant <twig:AvisUtilisateur /> pour chaque entrée. Ajoutez également un en-tête affichant le nom du produit et la note moyenne calculée en Twig.


Bonne pratique - Préfixe : pour les expressions Twig dynamiques

Dans les attributs d'un composant Twig, le préfixe : est indispensable pour passer des valeurs dynamiques : :note="a.note" évalue l'expression Twig et passe la valeur entière, tandis que note="a.note" passerait littéralement la chaîne "a.note". Utilisez le préfixe : pour toute variable, accès à un tableau ou expression booléenne.


Étape 6 — Vérifier la balise importmap et tester

Ouvrez templates/base.html.twig et vérifiez que la balise suivante est bien présente dans le <head> :

{{ importmap('app') }}

Lancez ensuite le serveur de développement :

symfony server:start

Accédez à https://127.0.0.1:8000/avis et vérifiez les points suivants :

  1. La note moyenne et les étoiles du résumé global s'affichent correctement en haut de page
  2. Chaque avis affiche le bon nombre d'étoiles jaunes (les étoiles inactives sont grises)
  3. L'avis avec une note de 2 affiche bien 2 étoiles jaunes et 3 grises
  4. L'avis avec une note de 5 affiche bien 5 étoiles jaunes
  5. Le composant est visuellement cohérent pour chaque avis (carte avec bordure et ombre)

Bonne pratique - Déboguer un Twig Component avec dump()

La fonction {{ dump() }} de Twig est votre meilleur allié pour déboguer un composant. Utilisez {{ dump(this.methode()) }} pour inspecter le retour d'une méthode, ou {{ dump(maPropriete) }} pour une propriété. Pensez toujours à retirer ces instructions avant de passer en production — elles exposent la structure interne de votre application.

Une solution complète du TP

Une solution

TP "Quiz Symfony UX"

Dans ce TP, vous allez créer de A à Z un composant Twig réutilisable nommé Alerte, capable d'afficher des messages de différents types (succès, erreur, avertissement, info), puis vous l'utiliserez dans une page de démonstration. Vous ajouterez ensuite un comportement de fermeture via un contrôleur Stimulus.


Étape 1 — Créer le projet et installer les dépendances

Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :

cd %USERPROFILE%\Documents
symfony new tp-twig-component --webapp
cd tp-twig-component
code .

Une fois VS Code ouvert, installez le package ux-twig-component :

composer require symfony/ux-twig-component

Vérifiez que l'installation s'est bien passée en cherchant symfony/ux-twig-component dans le fichier composer.json à la racine du projet.


Bonne pratique - Créer des projets Symfony avec --webapp

Le flag --webapp crée un projet Symfony complet avec Twig, AssetMapper, Stimulus et les outils de développement préconfigurés. C'est le point de départ recommandé pour tout projet web. Pour un microservice API sans interface, préférez symfony new mon-projet sans flag.


Étape 2 — Créer la classe PHP du composant Alerte

Créez le dossier src/Twig/Components/ s'il n'existe pas encore, puis créez-y le fichier Alerte.php.

Ce composant doit avoir :

  • Une propriété publique type de type string, avec la valeur par défaut 'info'
  • Une propriété publique message de type string, avec la valeur par défaut ''
  • Une propriété publique dismissible de type bool, avec la valeur par défaut true
  • Une méthode publique getCssClass() qui retourne une classe CSS selon le type :
    • 'success''alert-success'
    • 'error''alert-danger'
    • 'warning''alert-warning'
    • tout autre type → 'alert-info'

Bonne pratique - Utiliser match() plutôt que switch en PHP 8+

L'expression match de PHP 8.0 est plus concise et plus sûre que switch : elle utilise une comparaison stricte (===), retourne une valeur directement, et lève une UnhandledMatchError si aucun cas ne correspond (contrairement à switch qui passe silencieusement). Utilisez match pour les mappages simples valeur → valeur.


Étape 3 — Créer le template Twig du composant

Créez le fichier templates/components/Alerte.html.twig. Ce template doit :

  • Afficher une <div> avec les classes alert et la classe retournée par getCssClass()
  • Afficher le message dans la div
  • Si dismissible est vrai, afficher un bouton de fermeture <button> avec data-action="click->alerte#fermer"
  • Entourer le tout d'un <div data-controller="alerte"> pour permettre à Stimulus d'agir

Bonne pratique - Nommage des cibles Stimulus (data-*-target)

La convention de nommage Stimulus pour les cibles est data-[nom-contrôleur]-target="[nom-cible]". Si le contrôleur s'appelle alerte et la cible boite, l'attribut est data-alerte-target="boite". Dans le contrôleur JS, Stimulus génère automatiquement this.boiteTarget (singulier) et this.boiteTargets (pluriel si plusieurs éléments).


Étape 4 — Créer le contrôleur Stimulus pour la fermeture

Créez le fichier assets/controllers/alerte_controller.js. Ce contrôleur doit :

  • Déclarer une cible boite
  • Exposer une méthode fermer() qui masque l'élément cible boite en lui appliquant display: none

Bonne pratique - Nommage des fichiers contrôleurs Stimulus

La convention Stimulus est nom_controller.js avec des underscores. Le nom du contrôleur (utilisé dans data-controller) est déduit du nom de fichier en retirant _controller.js. Ainsi alerte_controller.jsdata-controller="alerte". Les tirets sont aussi supportés : mon-composant_controller.jsdata-controller="mon-composant".


Étape 5 — Créer le contrôleur Symfony et la page de démonstration

Générez un contrôleur Symfony nommé AlerteDemoController :

php bin/console make:controller AlerteDemoController

Modifiez src/Controller/AlerteDemoController.php pour passer au template un tableau de messages avec leurs types :

$alertes = [
['type' => 'success', 'message' => 'L\'opération a été réalisée avec succès.'],
['type' => 'error', 'message' => 'Une erreur critique est survenue.'],
['type' => 'warning', 'message' => 'Attention, cette action est irréversible.'],
['type' => 'info', 'message' => 'Une mise à jour est disponible.'],
];

Transmettez ce tableau au template via render().


Bonne pratique - Attribut #[Route] en PHP 8

Depuis Symfony 6, la syntaxe d'attribut PHP #[Route('/chemin', name: 'nom_route')] remplace les annotations Doctrine @Route. Toujours nommer ses routes avec name: — cela permet d'utiliser $this->generateUrl('nom_route') ou path('nom_route') en Twig, découplant le code des URLs réelles.


Étape 6 — Appeler le composant dans le template

Modifiez templates/alerte_demo/index.html.twig pour itérer sur le tableau alertes et appeler le composant <twig:Alerte /> pour chaque entrée. Utilisez le préfixe : pour passer les valeurs dynamiques.


Bonne pratique - Booléens dans les attributs de composants Twig

Ne jamais passer des booléens sans le préfixe :. Sans :, dismissible="false" passe la chaîne "false" (valeur truthy en PHP !), pas le booléen false. Avec :dismissible="false", Twig évalue l'expression et passe le vrai booléen false. Idem pour true, null, et les nombres.


Étape 7 — Tester et vérifier le comportement

Vérifiez d'abord la présence de {{ importmap('app') }} dans templates/base.html.twig, puis lancez le serveur :

symfony server:start

Accédez à https://127.0.0.1:8000/alerte-demo. Vérifiez les points suivants :

  1. Les quatre alertes s'affichent avec des couleurs différentes selon leur type
  2. Cliquer sur le bouton × fait disparaître l'alerte sans rechargement de page
  3. La cinquième alerte (non fermable) n'affiche pas de bouton ×
  4. Ouvrir la console du navigateur (F12) et vérifier l'absence d'erreurs JavaScript

Bonne pratique - importmap('app') et AssetMapper

La balise {{ importmap('app') }} dans le <head> est le pont entre PHP et JavaScript avec AssetMapper. Elle génère une Import Map HTML qui permet au navigateur de résoudre les imports ES modules sans bundler. Sans elle, aucun contrôleur Stimulus ne se charge. C'est l'équivalent du tag encore_entry_link_tags() de Webpack Encore.

Une solution complète du TP

Une solution

TP "Slide Symfony UX"

Dans ce TP, vous allez créer un composant Twig réutilisable nommé Slide, capable d'afficher un diaporama de slides navigables grâce à des boutons "Précédent" et "Suivant". La navigation entre les slides sera gérée par un contrôleur Stimulus, sans rechargement de page. Le composant sera ensuite utilisé dans une page de démonstration.


Étape 1 — Créer le projet et installer les dépendances

Ouvrez un terminal PowerShell ou le terminal intégré de VS Code (Ctrl + ù), naviguez vers votre dossier Documents et créez un nouveau projet Symfony :

cd %USERPROFILE%\Documents
symfony new tp-slide-component --webapp
cd tp-slide-component
code .

Une fois VS Code ouvert sur le projet, installez le package ux-twig-component :

composer require symfony/ux-twig-component

Attendez la fin de l'installation, puis vérifiez que le package est bien présent dans composer.json.


Bonne pratique - Vérifier la structure d'un projet Symfony créé avec --webapp

Après symfony new --webapp, vérifiez systématiquement la présence de assets/controllers/ (pour vos contrôleurs Stimulus), templates/ (pour vos templates Twig) et importmap.php (pour les dépendances JavaScript). Ces dossiers et fichiers sont créés automatiquement par Flex et constituent la base de tout projet Symfony UX.


Étape 2 — Créer la classe PHP du composant Slide

Créez le fichier src/Twig/Components/Slide.php. Ce composant représente un diaporama. Il doit exposer :

  • Une propriété publique titre de type string, avec la valeur par défaut 'Diaporama'
  • Une propriété publique slides de type array, initialisée à un tableau vide. Chaque slide sera un tableau associatif avec les clés titre et contenu.
  • Une méthode publique getNombreSlides() qui retourne le nombre total de slides sous forme d'entier.

Bonne pratique - PHPDoc @var pour les tableaux typés

Le commentaire @var array<array{titre: string, contenu: string}> documente la structure attendue de chaque élément du tableau. Sans cela, les IDEs ne peuvent pas proposer l'autocomplétion sur les clés. C'est particulièrement important dans les Twig Components dont les propriétés sont des tableaux complexes transmis depuis des contrôleurs.


Étape 3 — Créer le template Twig du composant

Créez le fichier templates/components/Slide.html.twig. Ce template doit :

  • Afficher le titre du diaporama dans un <h2>
  • Afficher un compteur de slides (exemple : "Slide 1 / 3") dans un paragraphe utilisant une cible Stimulus compteur
  • Itérer sur les slides avec {% for index, slide in slides %} et afficher chaque slide dans une <div> avec data-slide-target="panneau"
  • Masquer par défaut tous les panneaux sauf le premier (via style="display: none" conditionnel)
  • Afficher deux boutons de navigation : "Précédent" et "Suivant", chacun avec data-action Stimulus approprié
  • Entourer le tout d'un <div data-controller="slide">

Bonne pratique - Stimulus Values pour passer des données PHP vers JavaScript

Le système values de Stimulus (static values = { total: Number }) est la méthode recommandée pour passer des données du serveur vers un contrôleur JS. L'attribut HTML data-[controller]-[name]-value est lié à this.[name]Value dans le contrôleur, avec conversion automatique de type (Number, String, Boolean, Array, Object). C'est plus robuste que de lire dataset manuellement.


Étape 4 — Créer le contrôleur Stimulus

Créez le fichier assets/controllers/slide_controller.js. Ce contrôleur doit :

  • Déclarer les cibles panneau et compteur
  • Déclarer une valeur total de type Number
  • Maintenir un index courant (initialisé à 0) dans une propriété d'instance
  • Exposer une méthode suivant() qui passe à la slide suivante (sans dépasser la dernière)
  • Exposer une méthode precedent() qui revient à la slide précédente (sans passer en dessous de 0)
  • Chaque changement de slide doit masquer l'ancienne, afficher la nouvelle, et mettre à jour le compteur

Bonne pratique - Centraliser la logique de transition dans Stimulus

Évitez de dupliquer la logique de changement d'état entre les méthodes suivant() et precedent(). Créez une méthode interne allerA(index) qui centralise la mise à jour du DOM. Cette approche facilite l'ajout futur d'une navigation directe (clic sur un indicateur de point) sans toucher aux méthodes existantes.


Étape 5 — Créer le contrôleur Symfony et préparer les données

Générez un contrôleur Symfony nommé SlideDemoController :

php bin/console make:controller SlideDemoController

Modifiez src/Controller/SlideDemoController.php pour préparer un tableau de slides sur le thème de Symfony UX et le transmettre au template :

$slides = [
['titre' => '...', 'contenu' => '...'],
// au moins 4 slides
];

Chaque slide doit avoir un titre court et un contenu de 1 à 2 phrases.


Bonne pratique - make:controller pour générer les contrôleurs

Utilisez toujours php bin/console make:controller NomController plutôt que de créer les fichiers manuellement. Cette commande crée automatiquement la classe contrôleur avec le bon namespace, l'action index() avec sa route, ET le template associé dans templates/. Cela évite les erreurs de nommage et respecte les conventions Symfony.


Étape 6 — Appeler le composant dans le template

Modifiez templates/slide_demo/index.html.twig pour appeler le composant <twig:Slide /> en lui passant le titre du diaporama et le tableau de slides.


Bonne pratique - Réutilisabilité d'un composant Twig

Un composant est véritablement réutilisable quand on peut l'instancier plusieurs fois sur la même page avec des données différentes, chaque instance étant totalement indépendante. Testez toujours cette réutilisabilité en ajoutant une deuxième instance dans votre template de test — si elles interfèrent, votre contrôleur Stimulus a probablement un état global au lieu d'un état d'instance.


Étape 7 — Vérifier la balise importmap et tester

Ouvrez templates/base.html.twig et vérifiez que la balise suivante est bien présente dans le <head> :

{{ importmap('app') }}

Lancez ensuite le serveur de développement et testez le composant dans le navigateur :

symfony server:start

Accédez à https://127.0.0.1:8000/slide-demo et vérifiez :

  1. La première slide s'affiche au chargement, les autres sont masquées
  2. Le compteur affiche "Slide 1 / 5"
  3. Le bouton "Suivant" avance à la slide suivante et met à jour le compteur
  4. Le bouton "Précédent" revient en arrière
  5. Les boutons ne dépassent pas les limites (pas de slide 0 ni de slide 6)
  6. La console du navigateur (F12) ne contient aucune erreur JavaScript

Bonne pratique - Indépendance des instances Stimulus

Chaque instance d'un contrôleur Stimulus est liée à un élément DOM distinct avec data-controller. Les propriétés d'instance comme indexCourant = 0 sont propres à chaque instance — deux diaporamas sur la même page ont chacun leur propre état. C'est pourquoi Stimulus est préféré à du JavaScript global qui partagerait un état commun entre instances.

Une solution complète du TP

Une solution